package com.jd.jim.gateway.service.impl;

import com.jd.jim.common.util.StringUtil;
import com.jd.jim.gateway.service.UserInfoCacheService;
import com.jd.jim.common.core.proxy.ProxyManager;
import com.jd.jim.common.enums.StatusEnum;
import com.jd.jim.common.exception.JIMException;
import com.jd.jim.common.pojo.JIMUserInfo;
import com.jd.jim.common.util.RouteInfoParseUtil;
import com.jd.jim.gateway.api.vo.req.ChatReqVO;
import com.jd.jim.gateway.api.vo.req.LoginReqVO;
import com.jd.jim.gateway.api.vo.res.RegisterInfoResVO;
import com.jd.jim.gateway.api.vo.res.JIMServerResVO;
import com.jd.jim.gateway.service.AccountService;
import com.jd.jim.server.api.ServerApi;
import com.jd.jim.server.api.vo.req.SendMsgReqVO;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import static com.jd.jim.common.enums.StatusEnum.OFF_LINE;
import static com.jd.jim.gateway.constant.Constant.ACCOUNT_PREFIX;
import static com.jd.jim.gateway.constant.Constant.ROUTE_PREFIX;

/**
 * @author ：sizegang
 * @description：
 * @version:  1.0
 */
@Service
@Slf4j
public class AccountServiceRedisImpl implements AccountService {
    private final static Logger LOGGER = LoggerFactory.getLogger(AccountServiceRedisImpl.class);

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private UserInfoCacheService userInfoCacheService;

    @Autowired
    private OkHttpClient okHttpClient;

    @Override
    public RegisterInfoResVO register(RegisterInfoResVO info) {
        String key = ACCOUNT_PREFIX + info.getUserId();

        String name = redisTemplate.opsForValue().get(info.getUserName());
        if (null == name) {
            //为了方便查询，冗余一份 有时候会根据 姓名查询 userId
            redisTemplate.opsForValue().set(key, info.getUserName());
            redisTemplate.opsForValue().set(info.getUserName(), key);
        } else {
            long userId = Long.parseLong(name.split(":")[1]);
            info.setUserId(userId);
            info.setUserName(info.getUserName());
        }

        return info;
    }

    @Override
    public StatusEnum login(LoginReqVO loginReqVO) throws Exception {
        //去Redis里查询 如果redis中存在则说明已经注册了那么可以登录成功。 微信等这里肯定有大量的登录逻辑校验
        // 可以处理一些其他的业务 例如用户态校验等
        String key = ACCOUNT_PREFIX + loginReqVO.getUserId();
        String userName = redisTemplate.opsForValue().get(key);
        if (StringUtil.isEmpty(userName)) {
            log.info("用户登录失败， redis中未存在当前用户路由信息");
            return StatusEnum.ACCOUNT_NOT_MATCH;
        }

        if (!userName.equals(loginReqVO.getUserName())) {
            return StatusEnum.ACCOUNT_NOT_MATCH;
        }

        //登录成功，保存登录状态 这里也可以进行是否重复登录校验
        boolean status = userInfoCacheService.saveAndCheckUserLoginStatus(loginReqVO.getUserId());
        // 不校验重复登录情况
//        if (status == false) {
//            //重复登录
//            return StatusEnum.REPEAT_LOGIN;
//        }

        return StatusEnum.SUCCESS;
    }

    @Override
    public void saveRouteInfo(LoginReqVO loginReqVO, String msg) throws Exception {
        String key = ROUTE_PREFIX + loginReqVO.getUserId();
        redisTemplate.opsForValue().set(key, msg);
    }

    @Override
    public Map<Long, JIMServerResVO> loadRouteRelated() {

        Map<Long, JIMServerResVO> routes = new HashMap<>(64);
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        ScanOptions options = ScanOptions.scanOptions()
                .match(ROUTE_PREFIX + "*")
                .build();
        Cursor<byte[]> scan = connection.scan(options);

        while (scan.hasNext()) {
            byte[] next = scan.next();
            String key = new String(next, StandardCharsets.UTF_8);
            LOGGER.info("key={}", key);
            // 构造IM Server 地址  IP:port  because  redis中存在的是 key: jim-route:1639215209688    value : 192.168.0.105:9002:8082
            parseServerInfo(routes, key);

        }
        try {
            scan.close();
        } catch (IOException e) {
            LOGGER.error("IOException", e);
        }

        return routes;
    }

    /**
     * 从redis中获取 当前userId用户 所命中的 netty服务器
     * @param userId
     * @return
     */
    @Override
    public JIMServerResVO loadRouteRelatedByUserId(Long userId) {
        String value = redisTemplate.opsForValue().get(ROUTE_PREFIX + userId);

        if (value == null) {
            // 用户不在线异常
            throw new JIMException(OFF_LINE);
        }
        // 构造IM Server 地址  IP:port  because  redis中存在的是 key: jim-route:1639215209688    value : 192.168.0.105:9002:8082
        JIMServerResVO JIMServerResVO = new JIMServerResVO(RouteInfoParseUtil.parse(value));
        return JIMServerResVO;
    }

    // 构造命中的IM server 的ip:port
    private void parseServerInfo(Map<Long, JIMServerResVO> routes, String key) {
        long userId = Long.valueOf(key.split(":")[1]);
        String value = redisTemplate.opsForValue().get(key);
        JIMServerResVO JIMServerResVO = new JIMServerResVO(RouteInfoParseUtil.parse(value));
        routes.put(userId, JIMServerResVO);
    }


    /**
     * 发送消息
     * @param JIMServerResVO
     * @param sendUserId     发送者的ID
     * @param groupReqVO     消息
     * @throws Exception
     */
    @Override
    public void pushMsg(JIMServerResVO JIMServerResVO, long sendUserId, ChatReqVO groupReqVO) throws Exception {
        JIMUserInfo timUserInfo = userInfoCacheService.loadUserInfoByUserId(sendUserId);

        String url = "http://" + JIMServerResVO.getIp() + ":" + JIMServerResVO.getHttpPort();
        ServerApi serverApi = new ProxyManager<>(ServerApi.class, url, okHttpClient).getInstance();
        SendMsgReqVO vo = new SendMsgReqVO(timUserInfo.getUserName() + ":" + groupReqVO.getMsg(), groupReqVO.getUserId());
        Response response = null;
        try {
            response = (Response) serverApi.sendMsg(vo);
        } catch (Exception e) {
            LOGGER.error("Exception", e);
        } finally {
            response.body().close();
        }
    }

    /**
     * 用户下线 清空登录状态
     * @param userId 下线用户ID
     * @throws Exception
     */
    @Override
    public void offLine(Long userId) throws Exception {

        // TODO 这里需要用lua保证原子性

        //删除路由
        redisTemplate.delete(ROUTE_PREFIX + userId);

        //删除登录状态
        userInfoCacheService.removeLoginStatus(userId);
    }
}
